package iso3.pt.dao;

import iso3.pt.model.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class PtDAO implements IPtDAO
{
	private static PtDAO instance = null;
	private static HashMap<Integer, Asignatura> cacheAsignaturas = null;

	static SessionFactory sessionFactory;
	static Session session;

	protected PtDAO()
	{
		// Exists only to defeat instantiation.
	}

	/**
	 * Comprobado.
	 */
	public static PtDAO getInstance()
	{
		if (instance == null)
		{
			instance = new PtDAO();

			sessionFactory = new Configuration().configure()
					.buildSessionFactory();
			session = sessionFactory.openSession();
			Transaction tx = session.beginTransaction();

			cacheAsignaturas = new HashMap<Integer, Asignatura>();

			@SuppressWarnings("unchecked")
			List<Asignatura> asignaturas = session.createQuery(
					"from Asignatura").list();

			for (Iterator<Asignatura> iter = asignaturas.iterator(); iter
					.hasNext();)
			{
				Asignatura asi = iter.next();
				// System.out.println(asi.getNombre() + " " +
				// asi.getCreditos());
				cacheAsignaturas.put(asi.getId(), asi);
			}

			tx.commit();
			// session.close(); // No hay que cerrar la sesion sino se pierden
			// las relaciones
		}
		return instance;
	}

	/**
	 * @author ander
	 * Comprobado.
	 */
	@Override
	public Profesor getProfesor(int idAsignatura)
	{
		return cacheAsignaturas.get(idAsignatura).getProfesor();
	}

	/**
	 * @author ander
	 * Comprobado
	 */
	@Override
	public Set<Alumno> getAlumnos(int idAsignatura)
	{
		return cacheAsignaturas.get(idAsignatura).getAlumnos();
	}

	/**
	 * @author ander
	 * Comprobado.
	 */
	@Override
	public List<Evaluacion> getEvaluacionesOrderedByAsignatura(int idAlumno)
	{
		List<Evaluacion> resultado = new ArrayList<Evaluacion>();
		
		for (Integer idAsignatura : cacheAsignaturas.keySet())
		{
			Set<Evaluacion> evaluaciones = getEvaluaciones(idAsignatura, idAlumno);
			for (Evaluacion evaluacion : evaluaciones)
			{
				resultado.add(evaluacion);
			}
		}
		return resultado;
	}

	/**
	 * @author ander
	 * Comprobado.
	 */
	@Override
	public Set<Evaluacion> getEvaluaciones(int idAsignatura, int idAlumno)
	{
		Set<Evaluacion> rdo = new HashSet<Evaluacion>();

		Alumno a;
		try
		{
			a = getAlumno(idAlumno);
		}
		catch (UserNotFoundException e1)
		{
			e1.printStackTrace();
			return rdo;
		}

		Set<Evaluacion> e = a.getEvaluaciones();
		for (Evaluacion ev : e)
		{
			if (ev.getAsignatura().getCodigo() == idAsignatura)
				rdo.add(ev);
		}
		return rdo;
	}

	/**
	 * @author jose
	 * @throws UserNotFoundException
	 * Comprobado.
	 */
	@Override
	public void addEvaluacion(String concepto, float nota, int idAsignatura,
			int idAlumno) throws UserNotFoundException
	{
		Evaluacion eva = new Evaluacion(concepto, nota);

		Alumno alu = getAlumno(idAlumno);

		Transaction tx = session.beginTransaction();

		eva.setAsignatura(cacheAsignaturas.get(idAsignatura));
		eva.setAlumno(alu);
		alu.addEvaluacion(eva);
		session.save(eva);

		tx.commit();
	}

	/**
	 * @author ander
	 * Comprobado
	 */
	@Override
	public Set<Unidad> getUnidades(int idAsignatura)
	{
		Asignatura asig = cacheAsignaturas.get(idAsignatura);
		return asig.getUnidades();
	}

	/**
	 * @author ander
	 * Comprobado.
	 */
	@Override
	public Set<Asignatura> getAsignaturas()
	{
		Set<Asignatura> asignaturas = new HashSet<Asignatura>();
		for (Entry<Integer, Asignatura> asignatura : cacheAsignaturas
				.entrySet())
		{
			asignaturas.add(asignatura.getValue());
		}

		return asignaturas;
	}

	/**
	 * @author Jose
	 * Comprobado.
	 */
	@Override
	public Alumno getAlumno(int id) throws UserNotFoundException
	   {		
		   Alumno alu = null;
		   boolean encontrado = false;
		   Iterator<Alumno> iterAlumnos = null;
		   
		   	Iterator<Entry<Integer, Asignatura>> iterAsignaturas = cacheAsignaturas.entrySet().iterator();
		   
		   	while (iterAsignaturas.hasNext() && encontrado == false)
		   	{
		   		iterAlumnos = iterAsignaturas.next().getValue().getAlumnos().iterator();
		   		
		   		while (iterAlumnos.hasNext() && encontrado == false)
		   		{	
		   			alu = iterAlumnos.next();
		   			
		   			if (alu.getDni() == id)
		   			{
		   				encontrado = true;
		   			}
		   		}
		   	}
		   	
		   	if (encontrado == false)
			{
		   		Transaction tx = session.beginTransaction();
	           
		   		@SuppressWarnings("unchecked")
		   		List<Alumno> alumnos = session.createQuery("from Alumno").list();
	           
		   		iterAlumnos = alumnos.iterator();
	           
		   		while (iterAlumnos.hasNext() && encontrado == false) 
		   		{
		   			alu = iterAlumnos.next();
	        	   
		   			if (alu.getDni() == id)
		   			{
		   				encontrado = true;
		   			}
		   		}
	           
		   		tx.commit();
		   		
		   		if (encontrado == false)
		   		{
		   			throw new UserNotFoundException();
		   		}
			}
		   	
			return alu;
	   }

	/**
	 * @author ander
	 * Comprobado (funciona en getEvaluaciones())
	 */
	@Override
	public Asignatura getAsignatura(int id)
	{
		return cacheAsignaturas.get(id);
	}

	/**
	 * @author jose
	 * Comprobado
	 */
	@Override
	public Alumno loginAlumno(int dni, String pass)
			throws UserNotFoundException, IncorrectPasswordException
	{
		Alumno alu = getAlumno(dni);

		if (alu.getPassword().equals(pass) == false)
		{
			throw new IncorrectPasswordException();
		}

		return alu;
	}

	/**
	 * @author Jose
	 * Comprobado.
	 */
	@Override
	public Set<Asignatura> getAsignaturas(int idAlumno)
	{
		Set<Asignatura> asignaturas = new HashSet<Asignatura>();

		for (Entry<Integer, Asignatura> asignatura : cacheAsignaturas
				.entrySet())
		{
			boolean encontrado = false;
			Iterator<Alumno> iter = asignatura.getValue().getAlumnos()
					.iterator();

			while (iter.hasNext() && encontrado == false)
			{
				Alumno alu = iter.next();

				if (alu.getDni() == idAlumno)
				{
					encontrado = true;
					asignaturas.add(asignatura.getValue());
				}
			}
		}

		return asignaturas;
	}

	/**
	 * @author jose
	 * @throws UserNotFoundException
	 * Comprobado.
	 */
	@Override
	public void matricular(int idAlumno, int idAsignatura)
			throws UserNotFoundException
	{
		Asignatura asi = cacheAsignaturas.get(idAsignatura);
		Alumno alu = getAlumno(idAlumno);

		Transaction tx = session.beginTransaction();

		asi.addAlumno(alu);
		alu.addAsignatura(asi);
		session.save(asi);
		session.save(alu);

		tx.commit();
	}

	/**
	 * @author jose
	 * @throws UserNotFoundException
	 * Comprobado.
	 */
	@Override
	public void desmatricular(int idAlumno, int idAsignatura)
			throws UserNotFoundException
	{
		Asignatura asi = cacheAsignaturas.get(idAsignatura);
		Alumno alu = getAlumno(idAlumno);

		Transaction tx = session.beginTransaction();

		asi.removeAlumno(alu);
		alu.removeAsignatura(asi);
		session.save(asi);
		session.save(alu);

		tx.commit();
	}

	/**
	 * @author jose
	 * Comprobado.
	 */
	@Override
	public Profesor loginProfesor(int dni, String pass)
			throws UserNotFoundException, IncorrectPasswordException
	{
		Profesor prof = getProfesorByDni(dni);

		if (prof.getPassword().equals(pass) == false)
		{
			throw new IncorrectPasswordException();
		}

		return prof;
	}

	/**
	 * Comprobado.
	 *	//TODO Preguntarle si es por id de Profesor (1) o DNI (1111111)
	 */
	@Override
	public Set<Asignatura> getAsignaturasProfesor(int idProfesor)
	{
		Set<Asignatura> asignaturas = new HashSet<Asignatura>();

		for (Entry<Integer, Asignatura> asignatura : cacheAsignaturas
				.entrySet())
		{
			if (asignatura.getValue().getProfesor().getId() == idProfesor)
			{
				asignaturas.add(asignatura.getValue());
			}
		}

		return asignaturas;
	}

	/**
	 * Comprobado.
	 */
	@Override
	public Profesor getProfesorByDni(int dni) throws UserNotFoundException
	{
		Profesor prof = null;
		boolean encontrado = false;

		Iterator<Entry<Integer, Asignatura>> iter = cacheAsignaturas.entrySet()
				.iterator();

		while (iter.hasNext() && encontrado == false)
		{
			prof = iter.next().getValue().getProfesor();

			if (prof.getDni() == dni)
			{
				encontrado = true;
			}
		}

		if (encontrado == false)
		{
			throw new UserNotFoundException();
		}

		return prof;
	}

	/**
	 * @author ander
	 * Comprobado.
	 */
	@Override
	public List<Evaluacion> getEvaluacionesAsignatura(int idAsignatura)
	{
		List<Evaluacion> resultado = new ArrayList<Evaluacion>();
		
		Asignatura asig = cacheAsignaturas.get(idAsignatura);
		for (Alumno al : asig.getAlumnos())
		{
			Set<Evaluacion> evaluaciones = getEvaluaciones(idAsignatura, al.getDni());
			for (Evaluacion evaluacion : evaluaciones)
			{
				resultado.add(evaluacion);
			}
		}
		
		return resultado;
	}
	
	@Override
	public void addAlumno(Integer dni, String password, String nombre,
			String telefono)
	{		
		Alumno alumno = new Alumno(dni, password, nombre, telefono);
		
		Transaction tx = session.beginTransaction();
		session.save(alumno);
		tx.commit();		
	}
	
	@Override
	public void addUnidad(String acronimo, String titulo,
			String contenido, int idAsignatura)
	{
		Unidad unidad = new Unidad(acronimo, titulo, contenido);
		
		Asignatura asignatura = cacheAsignaturas.get(idAsignatura);
		asignatura.addUnidad(unidad);
		
		Transaction tx = session.beginTransaction();
		session.save(unidad);
		tx.commit();	
		
	}

	public static void main(String[] args) throws UserNotFoundException,
			IncorrectPasswordException
	{
		// Get instance
		getInstance();
		System.out.println("\nCache asignaturas:\n" + cacheAsignaturas);

		// Get profesor
		System.out.println("\nGet profesor:\n" + getInstance().getProfesor(1));

		// Get alumnos
		System.out.println("\nGet alumnos:\n" + getInstance().getAlumnos(1));

		// Add evaluación
		getInstance().addEvaluacion("Nota final", 10, 1, 1111);

		// Get unidades
		System.out.println("\nGet unidades:\n" + getInstance().getUnidades(1));

		// Get asignaturas
		System.out.println("\nGet asignaturas:\n"
				+ getInstance().getAsignaturas());

		// Desmatricular
		getInstance().desmatricular(1111, 1);
		System.out.println("\nDesmatricular:\n" + getInstance().getAlumnos(1));

		// Matricular
		getInstance().matricular(1111, 1);
		System.out.println("\nMatricular - Alumnos:\n" + getInstance().getAlumnos(1));

		// getEvaluacionesOrderedByAsignatura
		getInstance().getEvaluacionesOrderedByAsignatura(1111);
		System.out.println("\nEvaluaciones por asignatura:\n" + getInstance().getEvaluacionesOrderedByAsignatura(1111));

		// getEvaluaciones
		getInstance().getEvaluaciones(1, 1111);
		System.out.println("\nEvaluaciones:\n" + getInstance().getEvaluaciones(1, 1111));
		
		// // Get evaluaciones
		// System.out.println("\nGet evaluaciones asignatura y alumno:\n"+getInstance().getEvaluaciones(1,
		// 1));
		// 
		//
		//
		// Comprobar login profesor
		System.out.println("\nLogin profesor:\n"+getInstance().loginProfesor(1111111, "aaaa").toString());
		
		// getAsignaturasProfesor
		System.out.println("\nAsignaturas profesor 1 (id, NO dni):\n"+getInstance().getAsignaturasProfesor(1));
		
		// getProfesorByDni
		System.out.println("\nProfesor 1111111 (dni):\n"+getInstance().getProfesorByDni(1111111));

		//getEvaluacionesAsignatura
		System.out.println("\nGet evaluaciones asignatura:\n"+getInstance().getEvaluacionesAsignatura(1));
	}
}